home *** CD-ROM | disk | FTP | other *** search
/ ASME's Mechanical Engine…ing Toolkit 1997 December / ASME's Mechanical Engineering Toolkit 1997 December.iso / c_lang / msgraph.lzh / MGRAPH.C < prev    next >
Encoding:
C/C++ Source or Header  |  1987-05-04  |  17.1 KB  |  487 lines

  1. /* MGRAPH.C - Library of graphics functions for machines whose BIOS and
  2.  * display adapter hardware are compatible with that of the IBM PC.
  3.  * These routines are for use with the Microsoft C compiler version 4.0.
  4.  * All routines in this file were written by Paul Ketrick, and all
  5.  * comments and suggestions regarding these routines are welcome;
  6.  * please leave me a message on either Mike's C BBS at (619)722-8724
  7.  * or the Programmer's Forum at (818)701-1021. Feel free to use this
  8.  * library in any way you like, but please give me credit for it in
  9.  * your source code.
  10.  *
  11.  * Functions in this library:
  12.  * setvid()   - Sets the video display mode. Modes 0-7 as defined by IBM
  13.  *              are supported through the BIOS. This function also supports
  14.  *              modes 8 and 9 (as defined by me) which are 160x100 16-color
  15.  *              low-res graphics modes with colorburst output disabled and
  16.  *              enabled, respectively.
  17.  * pset()     - Plots a point on the screen at the given x-y coordinates
  18.  *              with the given color. Supports standard graphics modes 4-6
  19.  *              and also modes 8 and 9.
  20.  * line()     - Draws a line on the screen from one point to another.
  21.  *              Supports all graphics modes supported by the pset() routine.
  22.  * fline()    - Fast line-drawing routine. Only graphics modes 4-6 (the
  23.  *              320x200 and 640x200 modes) are currently supported by this
  24.  *              function.
  25.  * box()      - Draws the outline of a box on the screen. Supports all
  26.  *              graphics modes supported by the pset() routine.
  27.  * fbox()     - Fast box-drawing routine. Graphics modes 4-6 only are
  28.  *              supported.
  29.  * fillbox()  - Draws a filled-in box on the screen. Supports all graphics
  30.  *              modes supported by the pset() routine.
  31.  * ffillbox() - Fast filled-in box-drawing routine. Graphics modes 4-6
  32.  *              only are supported.
  33.  * circle()   - Draws a circle on the screen. Supports all graphics modes
  34.  *              supported by the pset() routine.
  35.  * fcircle()  - This is the fastest (and also probably the most compact)
  36.  *              of the three circle-drawing functions included in this
  37.  *              library; it works with all graphics modes supported
  38.  *              by the pset() routine.
  39.  * fellipse() - Fast ellipse-drawing routine used by the fcircle() routine
  40.  *              described above; works with all graphics modes supported
  41.  *              by the pset() function.
  42.  * cls()      - Clears the screen. Supports all graphics modes supported
  43.  *              by the setvid() routine.
  44.  *
  45.  *
  46.  * These routines are for use with the Microsoft C Compiler Version 4.0
  47.  * and the Microsoft Macro Assembler Version 4.0; they will probably
  48.  * work on older versions of the compiler and/or the assembler, but I
  49.  * have not experimented much. The box(), fbox(), fillbox(), ffillbox(),
  50.  * circle(), pcircle(), fcircle() and cls() functions are written entirely
  51.  * in C; the setvid() function is a combination of C and assembly language;
  52.  * and the pset(), line() and fline() routines are entirely in assembly
  53.  * language. I have not found that any special switches or options need
  54.  * to be used when compiling and assembling this code. LINK version
  55.  * 3.51 (supplied with the C compiler) should be used to link programs
  56.  * with this library.
  57.  *
  58.  * Function usage:
  59.  *
  60.  * void setvid(mode)
  61.  *   Sets the current video mode. Initialization of modes 0 through 7 is
  62.  *   performed entirely by the BIOS. Modes 8 and 9 are handled partially
  63.  *   through the BIOS and partially through assembly language code in
  64.  *   function init160(). The assembly language functions setpjmp() and
  65.  *   doint() are also used in the initialization of all graphics modes.
  66.  *
  67.  * void pset(x, y, color)
  68.  *   Plots a point with the specified color at coordinates (x,y) on the
  69.  *   screen; point (0,0) is at the top left corner of the screen. All
  70.  *   640x200, 320x200 and 160x100 graphics modes are supported.
  71.  *   No boundary checking is performed on the coordinates passed
  72.  *   to this routine; therefore points whose coordinates lie outside the
  73.  *   screen boundaries either will not be displayed or will be displayed
  74.  *   at incorrect locations on the screen. In the 320x200 modes the
  75.  *   color may be 0 to 3; in the 640x200 mode it may be 0 to 1; and
  76.  *   in the 160x100 modes it can range from 0 to 15. Only the
  77.  *   relevant bits of the color are used, however, so no masking off
  78.  *   of the color value need be done before passing it to this function.
  79.  *
  80.  * void line(x1, y1, x2, y2, color)
  81.  *   Draws a line on the screen in the specified color from coordinates
  82.  *   (x1,y1) to (x2,y2). Although this routine draws lines with average
  83.  *   speed it will work with whatever graphics modes are supported by the
  84.  *   pset() routine and should be good for display adapters up to
  85.  *   65536x65536 resolution.
  86.  *
  87.  * void fline(x1, y1, x2, y2, color)
  88.  *   Draws a line on the screen in the specified color from coordinates
  89.  *   (x1,y1) to (x2,y2). This routine only works with the 320x200 and
  90.  *   640x200 modes, but is faster than anything I've seen. It
  91.  *   averages around 100 vectors per second in the 640x200 mode on my
  92.  *   4.77 MHz PC with a NEC V20 processor installed.
  93.  *
  94.  * void box(x1, y1, x2, y2, color)
  95.  *   Draws the outline of a rectangle on the screen, two of
  96.  *   whose corners are at coordinates (x1,y1) and (x2,y2), in the
  97.  *   specified color. Like the line() routine, this routine supports
  98.  *   whatever graphics modes are supported by the pset() routine
  99.  *   which it calls.
  100.  *
  101.  * void fbox(x1, y1, x2, y2, color)
  102.  *   This routine is functionally the same as the box() routine but
  103.  *   uses the fline() function for much faster operation.
  104.  *
  105.  * void fillbox(x1, y1, x2, y2, color)
  106.  *   Draws a filled-in rectangle on the screen, two of whose corners
  107.  *   are at coordinates (x1,y1) and (x2,y2), in the specified color.
  108.  *   This routine is rather slow, but like the line() routine is
  109.  *   very general.
  110.  *
  111.  * void ffillbox(x1, y1, x2, y2, color)
  112.  *   This routine is functionally identical to the fillbox() routine
  113.  *   but uses the fline() function for much faster operation.
  114.  *
  115.  * void circle(x, y, radius, color)
  116.  *   Draws on the screen the outline of a circle with center coordinates
  117.  *   (x,y) and the given radius, in the specified color. This routine
  118.  *   is acceptably fast on 8087 systems but can be agonizingly slow on
  119.  *   on machines without a math coprocessor. It works with all graphics
  120.  *   modes supported by the pset() routine.
  121.  *
  122.  * void fcircle(x, y, radius, color)
  123.  *   This is a very fast circle-drawing routine. Its parameters are the
  124.  *   same as those of the circle() function above. It works with all
  125.  *   graphics modes supported by the pset() function.
  126.  *
  127.  * void fellipse(x, y, a, b, color)
  128.  *   This very fast ellipse-drawing function actually gets called by the
  129.  *   fcircle() routine described above. It draws an ellipse centered at
  130.  *   coordinates (x,y) with width 2*a and height 2*b in the specified
  131.  *   color. It works with all graphics modes supported by the pset()
  132.  *   function.
  133.  *
  134.  * void cls()
  135.  *   This routine has no parameters and simply calls the setvid()
  136.  *   routine without changing the current video mode, effectively
  137.  *   clearing the screeen.
  138.  */
  139.  
  140. #include <stdio.h>
  141. #include <math.h>                        /* Contains declarations for math
  142.                                          * functions.
  143.                                          */
  144.  
  145. #define PI 3.14159265359
  146. #define SWAP(x, y) {x-=y; y+=x; x=y-x;}    /* Swaps two integer variables without
  147.                                          * using a temporary variable.
  148.                                          */
  149.  
  150.  
  151. /*   E   X   T   E   R   N   A   L       V   A   R   I   A   B   L   E   S
  152.  *
  153.  * Note: The following global variables are all explicitly initialized so
  154.  * they will not be placed in a "FAR_BSS" segment by the compiler when the
  155.  * large-data memory models are used. Rather, they will be placed in the
  156.  * default (near) data segment so they can be accessed as near data by
  157.  * the assembly language routines.
  158.  */
  159.  
  160. unsigned int vidmseg=0;                    /* Holds video memory base segment. */
  161. int mode=0;                                /* Current video mode. */
  162. double aspect=0.0;                        /* Ratio of width of a horizontal
  163.                                          * dot to height of a vertical dot;
  164.                                          * for my screen, 2.0 in the 640x200
  165.                                          * mode, 1.0 for the 320x200 mode
  166.                                          * and 1.0 in the 160x100 mode worked
  167.                                          * best.
  168.                                          */
  169. int bitspixl=0;                            /* Holds number of bits per pixel in
  170.                                          * video memory.
  171.                                          */
  172.  
  173. /* Set the video display mode and initialize the video display hardware;
  174.  * supports the following video modes through the BIOS:
  175.  *        0=40x25 b/w    text            1=40x25 color text
  176.  *        2=80x25 b/w text            3=80x25 color text
  177.  *        4=320x200 b/w graphics        5=320x200 color graphics
  178.  *        6=640x200 b/w graphics
  179.  *        7=80x25 mono text
  180.  *
  181.  * Graphics modes unsupported by BIOS ROM:
  182.  *        8=160x100 16-color, colorburst disabled
  183.  *        9=160x100 16-color, colorburst enabled
  184.  */
  185.  
  186. setvid(newmode)
  187. int newmode;
  188.  
  189. {
  190.     int i;
  191.  
  192.     mode = newmode;
  193.  
  194.     if (mode!=8 && mode!=9)  {
  195.         doint(16, (0 << 8)+mode);        /* Call BIOS to initialize modes
  196.                                          * 0 through 7.
  197.                                          */
  198.         if (mode == 7)
  199.             vidmseg=0xb000;
  200.  
  201.         else  {
  202.             vidmseg=0xb800;
  203.  
  204.             if (mode == 4 || mode == 5)  {
  205.                 aspect = 1.0;            /* This may be changed to match a
  206.                                          * particular monitor.
  207.                                          */
  208.                 bitspixl = 2;
  209.             } else if (mode == 6)  {
  210.                 aspect = 2.0;            /* Same here. */
  211.                 bitspixl = 1;
  212.             }
  213.         }
  214.     } else  {                            /* Must be a 160x100 mode. */
  215.  
  216. /* The CGA's 160x100 16-color graphics mode requires some unconventional
  217.  * use of the CRT controller. This "graphics mode" is actually programmed
  218.  * as a text mode with a display 80 columns wide and 100 rows high, each
  219.  * character being 8 dots wide and 2 dots high (640x200 dots total). Since
  220.  * it is a text mode, 16 different colors can be displayed. The way 160-
  221.  * column graphics are acheived is by storing character DD hex (221 decimal)
  222.  * at every screen location. This graphics character appears as a solid
  223.  * rectangle which fills up the left half of its 8x8 character space
  224.  * (or, in this case, its 8x2 character space). During the time period
  225.  * in the raster scan that the left half of this character is being
  226.  * displayed, the character generator directs the foreground color of
  227.  * the particular character location to be displayed, and when the right
  228.  * half of the character is being displayed, the background color of the
  229.  * character location is outputted. Thus, setting the foreground or
  230.  * background color of a character location sets the color attribute of
  231.  * a single 4-dot by 2-dot block on the screen. Display memory mapping
  232.  * is an extension of the 80x25 text mode memory map: Even video memory
  233.  * addresses contain characters and odd memory addresses contain attribute
  234.  * bytes. The equation below converts x-y coordinates to video memory
  235.  * offsets:
  236.  *   offset = y * 160 + (x >> 1) * 2
  237.  * where x=0-159 and y=0-99. The only other special thing about this video
  238.  * mode is that the "blinking attribute bit" must be disabled; doing this
  239.  * enables the use of 16 background colors as well as 16 foreground colors.
  240.  */
  241.  
  242.         doint(16, (0 << 8) + mode - 6);    /* Call the BIOS to initialize the
  243.                                          * 80x25 b/w or color text mode.
  244.                                          */
  245.         vidmseg = 0xb800;
  246.         aspect = 1.0;                    /* This may be changed to match a
  247.                                          * particular monitor.
  248.                                          */
  249.         bitspixl = 4;
  250.         init160();                        /* Now call the assembly language
  251.                                          * routine to do the fancy stuff
  252.                                          * with the CRTC (documentation
  253.                                          * on this is in the file
  254.                                          * msgraph.asm).
  255.                                          */
  256.     }
  257.  
  258.     setpjmp(mode);                        /* Call assembly language routine to
  259.                                          * set up jump address for pset()
  260.                                          * (see pset() source in msgraph.asm)
  261.                                          */
  262. }
  263.  
  264. /*                 D  R  A  W     A     C  I  R  C  L  E
  265.  */
  266.  
  267. /* The following two routines draw a circle centered at the specified
  268.  * coordinates with the specified radius. Note that the circle's height
  269.  * will be equal to "radius" pixels while its width will be equal to
  270.  * (aspect * radius) pixels to compensate for the screen's aspect ratio.
  271.  */
  272.  
  273. /* The routine below draws circles using polar coordinates. It is
  274.  * roughly 20 times slower than the circle routine which follows it and
  275.  * is included only for the sheer heck of it.
  276.  */
  277.  
  278. void pcircle(x, y, radius, color)
  279. unsigned int x, y, radius, color;
  280.  
  281. {
  282.     float deg;
  283.  
  284.     for (deg=0.01; deg <= PI*2; deg+=PI/4/radius)
  285.         pset((int)(radius * cos(deg) * aspect + x),
  286.             (int)(radius * sin(deg) + y), color);
  287. }
  288.  
  289. /* The following routine draws a circle on the screen with the given center
  290.  * coordinates and radius. It runs pretty fast on systems with a math
  291.  * coprocessor, but may be too slow on machines without an 8087. However,
  292.  * it is probably about as fast as a floating-point-based circle-drawing
  293.  * routine will get.
  294.  */
  295.  
  296. void circle(x, y, radius, color)
  297. unsigned int x, y, radius, color;
  298.  
  299. {
  300.     int a, b, dx, dy;
  301.     int am = radius * sqrt(0.5) * aspect + 1;
  302.     int bm = radius * sqrt(0.5) + 1;
  303.     double aspect2 = aspect * aspect;
  304.     double xs=radius * radius * aspect2;
  305.     double ys=radius * radius;
  306.  
  307.     for (a=0; a < am; a++)  {
  308.         dy = sqrt(xs - a*a) / aspect;
  309.         pset(x + a, y + dy, color);
  310.         pset(x - a, y + dy, color);
  311.         pset(x + a, y - dy, color);
  312.         pset(x - a, y - dy, color);
  313.     }
  314.  
  315.     for (b=0; b < bm; b++)  {
  316.         dx = aspect * sqrt(ys - b*b);
  317.         pset(x + dx, y + b, color);
  318.         pset(x + dx, y - b, color);
  319.         pset(x - dx, y + b, color);
  320.         pset(x - dx, y - b, color);
  321.     }
  322. }
  323.  
  324. /* The fellipse() routine below draws on the graphics screen in the
  325.  * specified color an ellipse centered at coordinates (x,y) on the
  326.  * screen and having width 2*a and height 2*b. The fcircle() routine
  327.  * is a variation of the fellipse() function in which the width and
  328.  * height of the ellipse are equal.
  329.  *
  330.  * These routines were derived from Pascal ellipse-drawing routines
  331.  * in an article by Jerry Van Aken and Mark Novak in "ACM Transactions
  332.  * on Graphics," April, 1985, and draw circles faster and as good as
  333.  * or better than any other routine I have seen.
  334.  */
  335.  
  336. void fellipse(center_x, center_y, a0, b0, color)
  337. int center_x, center_y, a0, b0, color;
  338.  
  339. {
  340.     static long t1, t2, t3, t4, t5, t6, t7, t8, t9, d1, d2;
  341.     static long a, b;                    /* Hold long representations of
  342.                                          * a0, b0
  343.                                          */
  344.     register int x, y;
  345.  
  346.     a=a0; b=b0;
  347.     x=a; y=0;
  348.  
  349.     t1=a*a; t2=t1*2; t3=t2*2;
  350.     t4=b*b; t5=t4*2; t6=t5*2;
  351.     t7=t5*a; t8=t7*2; t9=0;
  352.  
  353.     d1=t2-t7+t4/2; d2=t1/2-t8+t5;
  354.  
  355.     while (d2 < 0)  {
  356.         pset(center_x+x, center_y+y, color);
  357.         pset(center_x-x, center_y+y, color);
  358.         pset(center_x+x, center_y-y, color);
  359.         pset(center_x-x, center_y-y, color);
  360.  
  361.         ++y;
  362.         t9 += t3;
  363.  
  364.         if (d1 < 0)  {
  365.             d1 += t9+t2;
  366.             d2 += t9;
  367.         } else  {
  368.             --x;
  369.             t8 -= t6;
  370.             d1 += t9-t8+t2;
  371.             d2 += t5-t8+t9;
  372.         }
  373.     }
  374.  
  375.     do  {
  376.         pset(center_x+x, center_y+y, color);
  377.         pset(center_x-x, center_y+y, color);
  378.         pset(center_x+x, center_y-y, color);
  379.         pset(center_x-x, center_y-y, color);
  380.  
  381.         --x;
  382.         t8 -= t6;
  383.  
  384.         if (d2 < 0)  {
  385.             ++y;
  386.             t9 += t3;
  387.             d2 += t5-t8+t9;
  388.         } else
  389.             d2 += t5-t8;
  390.  
  391.     } while (x >= 0);
  392. }
  393.  
  394. /* The following routine draws on the screen in the specified color a circle
  395.  * centered at the given coordinates and having the specified radius. Note
  396.  * that it enlarges the width of the circle to compensate for the monitor's
  397.  * aspect ratio.
  398.  */
  399.  
  400. void fcircle(center_x, center_y, radius, color)
  401. int center_x, center_y, radius, color;
  402.  
  403. {
  404.     fellipse(center_x, center_y, (int)(radius*aspect), radius, color);
  405. }
  406.  
  407.  
  408. /*                     D  R  A  W     A     B  O  X
  409.  */
  410.  
  411. /* This routine draws the outline of a rectangle on the screen, two of
  412.  * whose corners are located at (x1,y1) and (x2,y2).
  413.  */
  414.  
  415. void box(x1, y1, x2, y2, color)
  416. unsigned int x1, y1, x2, y2, color;
  417.  
  418. {
  419.     line(x1, y1, x2, y1, color);
  420.     line(x2, y1, x2, y2, color);
  421.     line(x1, y1, x1, y2, color);
  422.     line(x1, y2, x2, y2, color);
  423. }
  424.  
  425. /*                F  A  S  T     B  O  X     D  R  A  W
  426.  */
  427.  
  428. /* This routine is functionally identical to the one above, but it
  429.  * uses the fast line-drawing routine to draw the box.
  430.  */
  431.  
  432. void fbox(x1, y1, x2, y2, color)
  433. unsigned int x1, y1, x2, y2, color;
  434.  
  435. {
  436.     fline(x1, y1, x2, y1, color);
  437.     fline(x2, y1, x2, y2, color);
  438.     fline(x1, y1, x1, y2, color);
  439.     fline(x1, y2, x2, y2, color);
  440. }
  441.  
  442. /*             D  R  A  W     A     S  O  L  I  D     B  O  X
  443.  */
  444.  
  445. /* The following routine draws a solid rectangle on the screen, two of
  446.  * whose corners are located at (x1,y1) and (x2,y2), in the specified
  447.  * color.
  448.  */
  449.  
  450. fillbox(x1, y1, x2, y2, color)
  451. unsigned int x1, y1, x2, y2, color;
  452.  
  453. {
  454.     unsigned int y;
  455.  
  456.     if (y1 > y2)
  457.         SWAP(y1, y2);
  458.  
  459.     for (y=y1; y<=y2; y++)
  460.         line(x1, y, x2, y, color);
  461. }
  462.  
  463. /* The next routine is functionally identical to the one above, but
  464.  * uses the fline() routine for faster operation.
  465.  */
  466.  
  467. ffillbox(x1, y1, x2, y2, color)
  468. unsigned int x1, y1, x2, y2, color;
  469.  
  470. {
  471.     unsigned int y;
  472.  
  473.     if (y1 > y2)
  474.         SWAP(y1, y2);
  475.  
  476.     for (y=y1; y<=y2; y++)
  477.         fline(x1, y, x2, y, color);
  478. }
  479.  
  480. /*                    C  L  E  A  R     S  C  R  E  E  N
  481.  */
  482.  
  483. cls()
  484. {
  485.     setvid(mode);                        /* Easy clear-screen function */
  486. }
  487.